home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 10
/
AACD 10.iso
/
AACD
/
Magazine
/
Online
/
MagPLIP
/
source
/
server.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-04-01
|
33KB
|
1,158 lines
/*
** $VER: server.c 1.15 (01 Apr 1998)
**
** magplip.device - Parallel Line Internet Protocol
**
** Original code written by Oliver Wagner and Michael Balzer.
**
** This version has been completely reworked by Marius Gröger, introducing
** slight protocol changes. The new source is a lot better organized and
** maintainable.
**
** Additional changes and code cleanup by Jan Kratochvil and Martin Mares.
** The new source is significantly faster and yet better maintainable.
**
** (C) Copyright 1993-1994 Oliver Wagner & Michael Balzer
** (C) Copyright 1995 Jan Kratochvil & Martin Mares
** (C) Copyright 1995-1996 Marius Gröger
** All Rights Reserved
**
** $HISTORY:
**
** 01 Apr 1998 : 001.015 : integrated modifications for linPLIP from Stephane
** 19 Mar 1998 : 001.014 : fixed S2_ONLINE bug, which returned an
** error by a call to go online if it was
** already online. Report from Holger Kruse.
** Fixed by Stefan Ruppert.
** 29 Mar 1996 : 001.014 : changed copyright note
** 24 Feb 1996 : 001.013 : added PRTRSEL data direction signal
** 30 Dec 1995 : 001.012 : + dynamic allocation of only one frame buffer
** PLIP_MAXMTU now 128k
** + a bad MTU setting in ENV: will be
** forced to PLIP_MAXMTU instead of PLIP_DEFMTU
** + server task acknowledge now after calling
** readargs to avoid inconsistencies
** 03 Sep 1995 : 001.011 : hardware addressing nicer
** 30 Aug 1995 : 001.010 : + support for timer-timed timeout :-)
** + minor declaration related changes
** 20 Aug 1995 : 001.009 : support for ASM xfer routines
** removed obsolete CIA macros (mag/jk/mm)
** 29 Jul 1995 : 001.008 : support for arbitration delay
** symmetrical handling
** 26 Apr 1995 : 001.007 : _very_ nasty bug would miss packets and get
** the driver totally irritated
** 25 Apr 1995 : 001.006 : now compiles with ANSI and STRICT
** fixed bug with resource allocation
** 08 Mar 1995 : 001.005 : write req. are now handled by device.c
** 06 Mar 1995 : 001.004 : collision delay added
** 06 Mar 1995 : 001.003 : hardware transmission errors are no longer retried
** because this is any upper layers job
** 04 Mar 1995 : 001.002 : event tracking *much* more conform to SANA-2
** 18 Feb 1995 : 001.001 : startup now a bit nicer
** using BASEPTR
** 12 Feb 1995 : 001.000 : reworked original
*/
#define DEBUG 0
/*F*/ /* includes */
#ifndef CLIB_EXEC_PROTOS_H
#include <clib/exec_protos.h>
#include <pragmas/exec_sysbase_pragmas.h>
#endif
#ifndef CLIB_DOS_PROTOS_H
#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#endif
#ifndef CLIB_CIA_PROTOS_H
#include <clib/cia_protos.h>
#include <pragmas/cia_pragmas.h>
#endif
#ifndef CLIB_MISC_PROTOS_H
#include <clib/misc_protos.h>
#include <pragmas/misc_pragmas.h>
#endif
#ifndef CLIB_TIME_PROTOS_H
#include <clib/timer_protos.h>
#include <pragmas/timer_pragmas.h>
#endif
#ifndef CLIB_UTILITY_PROTOS_H
#include <clib/utility_protos.h>
#include <pragmas/utility_pragmas.h>
#endif
#ifndef EXEC_MEMORY_H
#include <exec/memory.h>
#endif
#ifndef EXEC_INTERRUPTS_H
#include <exec/interrupts.h>
#endif
#ifndef EXEC_DEVICES_H
#include <exec/devices.h>
#endif
#ifndef EXEC_IO_H
#include <exec/io.h>
#endif
#ifndef DEVICES_SANA2_H
#include <devices/sana2.h>
#endif
#ifndef HARDWARE_CIA_H
#include <hardware/cia.h>
#endif
#ifndef RESOURCES_MISC_H
#include <resources/misc.h>
#endif
#ifndef _STRING_H
#include <string.h>
#endif
#ifndef __MAGPLIP_H
#include "magplip.h"
#endif
#ifndef __DEBUG_H
#include "debug.h"
#endif
#ifndef __COMPILER_H
#include "compiler.h"
#endif
/*E*/
/*F*/ /* defines, types and enums */
/*
** return codes for arbitratedwrite()
*/
typedef enum { AW_OK, AW_ABORTED, AW_BUFFER_ERROR, AW_ERROR } AW_RESULT;
/* return val, cut to min or max if exceeding range */
#define BOUNDS(val, min, max) ((val) <= (max) ? ((val) >= (min) ? (val) :\
(min)) : (max))
/*E*/
/*F*/ /* imports */
/* external functions */
GLOBAL VOID dotracktype(BASEPTR, ULONG type, ULONG ps, ULONG pr, ULONG bs, ULONG br, ULONG pd);
GLOBAL VOID DevTermIO(BASEPTR, struct IOSana2Req *ios2);
GLOBAL USHORT ASM CRC16(REG(a0) UBYTE *, REG(d0) SHORT);
GLOBAL BOOL ASM hwsend(REG(a0) BASEPTR);
GLOBAL BOOL ASM hwrecv(REG(a0) BASEPTR);
GLOBAL VOID ASM interrupt(REG(a1) BASEPTR);
/* amiga.lib provides for these symbols */
GLOBAL FAR volatile struct CIA ciaa,ciab;
/*E*/
/*F*/ /* exports */
PUBLIC VOID SAVEDS ServerTask(void);
/*E*/
/*F*/ /* private */
PRIVATE struct PLIPBase *startup(void);
PRIVATE REGARGS VOID DoEvent(BASEPTR, long event);
PRIVATE VOID readargs(BASEPTR);
PRIVATE BOOL init(BASEPTR);
PRIVATE BOOL hwattach(BASEPTR);
PRIVATE VOID hwdetach(BASEPTR);
PRIVATE REGARGS BOOL goonline(BASEPTR);
PRIVATE REGARGS VOID gooffline(BASEPTR);
PRIVATE REGARGS AW_RESULT arbitratedwrite(BASEPTR, struct IOSana2Req *ios2);
PRIVATE REGARGS VOID dowritereqs(BASEPTR);
PRIVATE REGARGS VOID doreadreqs(BASEPTR);
PRIVATE REGARGS VOID dos2reqs(BASEPTR);
/*E*/
/*F*/ /* CIA access macros & functions */
#define CLEARINT SetICR(CIAABase, CIAICRF_FLG)
#define DISABLEINT AbleICR(CIAABase, CIAICRF_FLG)
#define ENABLEINT AbleICR(CIAABase, CIAICRF_FLG | CIAICRF_SETCLR)
#ifdef LINPLIP
/* Most of these defines have no meaning at all, but it's safe ... */
# define SETCIAOUTPUT
# define SETCIAINPUT
# define PARINIT(b) ciaa.ciaddrb = 0xff; ciaa.ciaprb = 0x00
/* Good line : SELECT=POUT=PUSY=0 */
# define TESTLINE(b) ((ciab.ciapra&(CIAF_PRTRSEL|CIAF_PRTRPOUT|CIAF_PRTRBUSY)) || pb->pb_Flags&PLIPF_RECEIVING)
# define SETREQUEST(b)
# define CLEARREQUEST(b)
#else
# define SETCIAOUTPUT ciab.ciapra |= CIAF_PRTRSEL; ciaa.ciaddrb = 0xFF
# define SETCIAINPUT ciab.ciapra &= ~CIAF_PRTRSEL; ciaa.ciaddrb = 0x00
# define PARINIT(b) SETCIAINPUT; \
ciab.ciaddra &= ~((b)->pb_HandshakeMask[HS_LINE]); \
ciab.ciaddra |= (b)->pb_HandshakeMask[HS_REQUEST]
# define TESTLINE(b) (ciab.ciapra & (b)->pb_HandshakeMask[HS_LINE])
# define SETREQUEST(b) ciab.ciapra |= (b)->pb_HandshakeMask[HS_REQUEST]
# define CLEARREQUEST(b) ciab.ciapra &= ~((b)->pb_HandshakeMask[HS_REQUEST])
#endif
/*E*/
/*
** functions to gain/release hardware, initialise communication
** and for xmission timeout handling
*/
/*F*/ PRIVATE BOOL hwattach(BASEPTR)
{
BOOL rc = FALSE;
d(("entered\n"));
if (MiscBase = OpenResource("misc.resource"))
{
if (CIAABase = OpenResource("ciaa.resource"))
{
CiaBase = CIAABase;
d(("ciabase is %lx\n",CiaBase));
/* obtain exclusive access to the parallel hardware */
if (!AllocMiscResource(MR_PARALLELPORT, pb->pb_DevNode.lib_Node.ln_Name))
{
pb->pb_AllocFlags |= 1;
if (!AllocMiscResource(MR_PARALLELBITS, pb->pb_DevNode.lib_Node.ln_Name))
{
pb->pb_AllocFlags |= 2;
/* Add our interrupt to handle CIAICRB_FLG.
** This is also cia.resource means of granting exclusive
** access to the related registers in the CIAs.
*/
pb->pb_Interrupt.is_Node.ln_Type = NT_INTERRUPT;
pb->pb_Interrupt.is_Node.ln_Pri = 127;
pb->pb_Interrupt.is_Node.ln_Name = SERVERTASKNAME;
pb->pb_Interrupt.is_Data = (APTR)pb;
pb->pb_Interrupt.is_Code = (VOID (*)())&interrupt;
/* We must Disable() bcos there could be an interrupt already
** waiting for us. We may, however, not Able/SetICR() before
** we have access!
*/
Disable();
if (!AddICRVector(CIAABase, CIAICRB_FLG, &pb->pb_Interrupt))
{
DISABLEINT; /* this is what I meant */
rc = TRUE;
}
Enable();
if (rc)
{
pb->pb_AllocFlags |= 4;
PARINIT(pb); /* cia to input, handshake in/out setting */
CLEARREQUEST(pb); /* setup handshake lines */
CLEARINT; /* clear this interrupt */
ENABLEINT; /* allow interrupts */
}
}
else
d(("no parallelbits\n"));
}
else
d(("no parallelport\n"));
}
else
d(("no misc resource\n"));
}
else
d(("no misc resource\n"));
return rc;
}
/*E*/
/*F*/ PRIVATE VOID hwdetach(BASEPTR)
{
if (pb->pb_AllocFlags & 4)
{
DISABLEINT;
CLEARINT;
RemICRVector(CIAABase, CIAICRB_FLG, &pb->pb_Interrupt);
}
if (pb->pb_AllocFlags & 2) FreeMiscResource(MR_PARALLELBITS);
if (pb->pb_AllocFlags & 1) FreeMiscResource(MR_PARALLELPORT);
pb->pb_AllocFlags = 0;
}
/*E*/
/*F*/ PRIVATE ULONG ASM SAVEDS exceptcode(REG(d0) ULONG sigmask, REG(a1) BASEPTR)
{
/*extern void KPrintF(char *,...);
KPrintF("exceptcode\n");*/
/* remove the I/O Block from the port */
WaitIO((struct IORequest*)&pb->pb_TimeoutReq);
/* this tells the xfer routines to cease polling */
pb->pb_TimeoutSet = 0xff;
return sigmask; /* re-enable the signal */
}
/*E*/
/*
** functions to go online/offline
*/
/*F*/ PRIVATE REGARGS VOID rejectpackets(BASEPTR)
{
struct IOSana2Req *ios2;
ObtainSemaphore(&pb->pb_WriteListSem);
while(ios2 = (struct IOSana2Req *)RemHead((struct List*)&pb->pb_WriteList))
{
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
DevTermIO(pb,ios2);
}
ReleaseSemaphore(&pb->pb_WriteListSem);
ObtainSemaphore(&pb->pb_ReadListSem);
while(ios2 = (struct IOSana2Req *)RemHead((struct List*)&pb->pb_ReadList))
{
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
DevTermIO(pb,ios2);
}
ReleaseSemaphore(&pb->pb_ReadListSem);
ObtainSemaphore(&pb->pb_ReadOrphanListSem);
while(ios2 = (struct IOSana2Req *)RemHead((struct List*)&pb->pb_ReadOrphanList))
{
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
DevTermIO(pb,ios2);
}
ReleaseSemaphore(&pb->pb_ReadOrphanListSem);
}
/*E*/
/*F*/ PRIVATE REGARGS BOOL goonline(BASEPTR)
{
BOOL rc = TRUE;
d(("trying to go online\n"));
if (pb->pb_Flags & (PLIPF_OFFLINE | PLIPF_NOTCONFIGURED))
{
if (!hwattach(pb))
{
d(("error going online\n"));
rc = FALSE;
}
else
{
GetSysTime(&pb->pb_DevStats.LastStart);
pb->pb_Flags &= ~(PLIPF_OFFLINE | PLIPF_NOTCONFIGURED);
DoEvent(pb, S2EVENT_ONLINE);
d(("i'm now online!\n"));
}
}
return rc;
}
/*E*/
/*F*/ PRIVATE REGARGS VOID gooffline(BASEPTR)
{
if (!(pb->pb_Flags & (PLIPF_OFFLINE | PLIPF_NOTCONFIGURED)))
{
hwdetach(pb);
pb->pb_Flags |= PLIPF_OFFLINE;
DoEvent(pb, S2EVENT_OFFLINE);
}
d(("ok!\n"));
}
/*E*/
/*
** SANA-2 Event management
*/
/*F*/ PRIVATE REGARGS VOID DoEvent(BASEPTR, long event)
{
struct IOSana2Req *ior, *ior2;
d(("event is %lx\n",event));
ObtainSemaphore(&pb->pb_EventListSem );
for(ior = (struct IOSana2Req *) pb->pb_EventList.lh_Head;
ior2 = (struct IOSana2Req *) ior->ios2_Req.io_Message.mn_Node.ln_Succ;
ior = ior2 )
{
if (ior->ios2_WireError & event)
{
Remove((struct Node*)ior);
DevTermIO(pb, ior);
}
}
ReleaseSemaphore(&pb->pb_EventListSem );
}
/*E*/
/*
** writing packets
*/
/*F*/ PRIVATE REGARGS AW_RESULT arbitratedwrite(BASEPTR, struct IOSana2Req *ios2)
{
BOOL having_line;
AW_RESULT rc;
struct PLIPFrame *frame = pb->pb_Frame;
/*
** Arbitration
** ===========================================================
**
** Pseudo code of the arbitration:
**
** if LINE is high (other side is ready to receive) then
** set REQUEST high (tell the other side we're ready to send)
** if LINE is high (other side is still ready to receive) then
** we have the line, do transfer
** reset REQUEST to low (tell other side we're ready to receive)
**
** AW_OK if we could transmit all the data correctly
** AW_BUFF_ERROR if the BufferManagement callback failed
** AW_ERROR if we got the line, but the actual transfer
** failed, perhaps due to a timeout
** AW_ABORT if we couldn't get the line
*/
#ifdef LINPLIP
having_line = !TESTLINE(pb);
if(!having_line)
{
if (!(pb->pb_Flags & PLIPF_RECEIVING))
CLEARREQUEST(pb); /* reset line state */
d2(("couldn't get the line\n"));
}
#else
having_line = FALSE;
if (!TESTLINE(pb)) /* is the line free ? */
{
SETREQUEST(pb); /* indicate our request to send */
#if 0
/*
** I have removed again the ARBITRATIONDELAY feature, although I am not
** really sure if this is a good thing. Anyway I didn't experience
** any more those nasty line errors, that initially made me implementing
** this. For now I've left the code here to let you play with it. Please
** report any comments concerning this.
**
** In fact the arbitration leaves a small door for undetected, real
** collisision, as the request lines are used for handshake during the
** transmission process. The CLEARREQUEST after NOT getting the line
** could be misinterpreted by the other side as the first handshake. Up
** to now I couldn't conceive a satisfying solution for this.
*/
if (pb->pb_ArbitrationDelay > 0)
{
pb->pb_CollReq.tr_time.tv_secs = 0;
pb->pb_CollReq.tr_time.tv_micro = pb->pb_ArbitrationDelay;
pb->pb_CollReq.tr_node.io_Command = TR_ADDREQUEST;
DoIO((struct IORequest*)&pb->pb_CollReq);
}
#endif
if (!TESTLINE(pb)) /* is the line still free ? */
having_line = TRUE;
else
{
if (!(pb->pb_Flags & PLIPF_RECEIVING))
CLEARREQUEST(pb); /* reset line state */
d2(("couldn't get the line-1\n"));
}
}
else d2(("couldn't get the line-2\n"));
#endif /* LINPLIP */
if (having_line)
{
struct BufferManagement *bm;
if (!(pb->pb_Flags & PLIPF_RECEIVING))
{
d(("having line for: type %08lx, size %ld\n",ios2->ios2_PacketType,
ios2->ios2_DataLength));
frame->pf_Type = ios2->ios2_PacketType;
frame->pf_Size = ios2->ios2_DataLength + PKTFRAMESIZE_2;
bm = (struct BufferManagement *)ios2->ios2_BufferManagement;
if (!(*bm->bm_CopyFromBuffer)((UBYTE*)(frame+1),
ios2->ios2_Data, ios2->ios2_DataLength))
{
rc = AW_BUFFER_ERROR;
CLEARREQUEST(pb); /* reset line state */
}
else
{
/* wait until I/O block is safe to be reused */
while(!pb->pb_TimeoutSet) Delay(1L);
pb->pb_TimeoutReq.tr_time.tv_secs = 0;
pb->pb_TimeoutReq.tr_time.tv_micro = pb->pb_Timeout;
pb->pb_TimeoutSet = 0;
SendIO((struct IORequest*)&pb->pb_TimeoutReq);
rc = hwsend(pb) ? AW_OK : AW_ERROR;
AbortIO((struct IORequest*)&pb->pb_TimeoutReq);
#if DEBUG&8
if(rc==AW_ERROR) d8(("Error sending packet (size=%ld)\n", (LONG)pb->pb_Frame->pf_Size));
#endif
}
}
else
{
d4(("arbitration error!\n"));
rc = AW_ABORTED;
}
}
else
rc = AW_ABORTED;
return rc;
}
/*E*/
/*F*/ PRIVATE REGARGS VOID dowritereqs(BASEPTR)
{
struct IOSana2Req *currentwrite, *nextwrite;
AW_RESULT code;
ObtainSemaphore(&pb->pb_WriteListSem);
for(currentwrite = (struct IOSana2Req *)pb->pb_WriteList.lh_Head;
nextwrite = (struct IOSana2Req *) currentwrite->ios2_Req.io_Message.mn_Node.ln_Succ;
currentwrite = nextwrite )
{
if (pb->pb_Flags & PLIPF_RECEIVING)
{
d(("incoming data!"));
break;
}
code = arbitratedwrite(pb, currentwrite);
if (code == AW_ABORTED) /* arbitration failed */
{
pb->pb_Flags |= PLIPF_COLLISION;
d(("couldn't get the line, trying again later\n"));
pb->pb_SpecialStats[S2SS_COLLISIONS].Count++;
d(("pb->pb_SpecialStats[S2SS_COLLISIONS].Count = %ld\n",pb->pb_SpecialStats[S2SS_COLLISIONS].Count));
if ((currentwrite->ios2_Req.io_Error++) > pb->pb_Retries)
{
pb->pb_SpecialStats[S2SS_TXERRORS].Count++;
d(("pb->pb_SpecialStats[S2SS_TXERRORS].Count = %ld\n",pb->pb_SpecialStats[S2SS_TXERRORS].Count));
currentwrite->ios2_Req.io_Error = S2ERR_TX_FAILURE;
currentwrite->ios2_WireError = S2WERR_TOO_MANY_RETIRES;
Remove((struct Node*)currentwrite);
DevTermIO(pb, currentwrite);
}
break;
}
else if (code == AW_BUFFER_ERROR) /* BufferManagement callback error */
{
d(("buffer error\n"));
DoEvent(pb, S2EVENT_ERROR | S2EVENT_BUFF | S2EVENT_SOFTWARE);
pb->pb_SpecialStats[S2SS_TXERRORS].Count++;
d(("pb->pb_SpecialStats[S2SS_TXERRORS].Count = %ld\n",pb->pb_SpecialStats[S2SS_TXERRORS].Count));
currentwrite->ios2_Req.io_Error = S2ERR_SOFTWARE;
currentwrite->ios2_WireError = S2WERR_BUFF_ERROR;
Remove((struct Node*)currentwrite);
DevTermIO(pb, currentwrite);
}
else if (code == AW_ERROR)
{
/*
** this is a real line error, upper levels (e.g. Internet TCP) have
** to care for reliability!
*/
d(("error while transmitting packet\n"));
DoEvent(pb, S2EVENT_ERROR | S2EVENT_TX | S2EVENT_HARDWARE);
pb->pb_SpecialStats[S2SS_TXERRORS].Count++;
d(("pb->pb_SpecialStats[S2SS_TXERRORS].Count = %ld\n",pb->pb_SpecialStats[S2SS_TXERRORS].Count));
currentwrite->ios2_Req.io_Error = S2ERR_TX_FAILURE;
currentwrite->ios2_WireError = S2WERR_GENERIC_ERROR;
Remove((struct Node*)currentwrite);
DevTermIO(pb, currentwrite);
}
else /*if (code == AW_OK)*/ /* well done! */
{
d(("packet transmitted successfully\n"));
pb->pb_DevStats.PacketsSent++;
dotracktype(pb, (ULONG) pb->pb_Frame->pf_Type, 1, 0, currentwrite->ios2_DataLength, 0, 0);
currentwrite->ios2_Req.io_Error = S2ERR_NO_ERROR;
currentwrite->ios2_WireError = S2WERR_GENERIC_ERROR;
Remove((struct Node*)currentwrite);
DevTermIO(pb, currentwrite);
}
}
ReleaseSemaphore(&pb->pb_WriteListSem);
}
/*E*/
/*
** reading packets
*/
/*F*/ PRIVATE REGARGS VOID doreadreqs(BASEPTR)
{
LONG datasize;
struct IOSana2Req *got;
ULONG pkttyp;
struct BufferManagement *bm;
BOOL rv;
struct PLIPFrame *frame = pb->pb_Frame;
/* wait until I/O block is safe to be reused */
while(!pb->pb_TimeoutSet) Delay(1L);
pb->pb_TimeoutReq.tr_time.tv_secs = 0;
pb->pb_TimeoutReq.tr_time.tv_micro = pb->pb_Timeout;
pb->pb_TimeoutSet = 0;
SendIO((struct IORequest*)&pb->pb_TimeoutReq);
rv = hwrecv(pb);
AbortIO((struct IORequest*)&pb->pb_TimeoutReq);
if (rv)
{
pb->pb_DevStats.PacketsReceived++;
datasize = frame->pf_Size - PKTFRAMESIZE_2;
dotracktype(pb, pkttyp = frame->pf_Type, 0, 1, 0, datasize, 0);
d(("packet %08lx, size %ld received\n",pkttyp,datasize));
ObtainSemaphore(&pb->pb_ReadListSem);
/* traverse the list of read-requests */
for(got = (struct IOSana2Req *)pb->pb_ReadList.lh_Head;
got->ios2_Req.io_Message.mn_Node.ln_Succ;
got = (struct IOSana2Req *)got->ios2_Req.io_Message.mn_Node.ln_Succ )
{
/* check if this one requests for the new packet we got */
if (got->ios2_PacketType == pkttyp )
{
Remove((struct Node*)got);
bm = (struct BufferManagement *)got->ios2_BufferManagement;
if (!(*bm->bm_CopyToBuffer)(got->ios2_Data, (UBYTE*)(frame+1), datasize))
{
d(("CopyToBuffer: error\n"));
got->ios2_Req.io_Error = S2ERR_SOFTWARE;
got->ios2_WireError = S2WERR_BUFF_ERROR;
DoEvent(pb, S2EVENT_ERROR | S2EVENT_BUFF | S2EVENT_SOFTWARE);
}
else
{
got->ios2_Req.io_Error = got->ios2_WireError = 0;
}
got->ios2_Req.io_Flags = 0;
#ifndef LINPLIP
memcpy(got->ios2_SrcAddr, pb->pb_SrcAddr, PLIP_ADDRFIELDSIZE);
memcpy(got->ios2_DstAddr, pb->pb_DstAddr, PLIP_ADDRFIELDSIZE);
#endif
got->ios2_DataLength = datasize;
d(("packet received, satisfying S2Request\n"));
DevTermIO(pb, got);
got = NULL;
break;
}
}
ReleaseSemaphore(&pb->pb_ReadListSem);
}
else
{
d8(("Error receiving (%ld. len=%ld)\n", rv, frame->pf_Size));
/* something went wrong during receipt */
DoEvent(pb, S2EVENT_HARDWARE | S2EVENT_ERROR | S2EVENT_RX);
got = NULL;
pb->pb_DevStats.BadData++;
}
/* If no one wanted this packet explicitely, there is one chance
** left: somebody waiting for orphaned packets. If this fails, too,
** we will drop it.
*/
if (got)
{
d(("unknown packet\n"));
pb->pb_DevStats.UnknownTypesReceived++;
ObtainSemaphore(&pb->pb_ReadOrphanListSem);
got = (struct IOSana2Req *)RemHead((struct List*)&pb->pb_ReadOrphanList);
ReleaseSemaphore(&pb->pb_ReadOrphanListSem);
if (got)
{
bm = (struct BufferManagement *)got->ios2_BufferManagement;
if (!(*bm->bm_CopyToBuffer)(got->ios2_Data, (UBYTE*)(frame+1), datasize))
{
got->ios2_Req.io_Error = S2ERR_SOFTWARE;
got->ios2_WireError = S2WERR_BUFF_ERROR;
}
else
{
got->ios2_Req.io_Error = got->ios2_WireError = 0;
}
got->ios2_Req.io_Flags = 0;
#ifndef LINPLIP
memcpy(got->ios2_SrcAddr, pb->pb_SrcAddr, PLIP_ADDRFIELDSIZE);
memcpy(got->ios2_DstAddr, pb->pb_DstAddr, PLIP_ADDRFIELDSIZE);
#endif
got->ios2_DataLength = datasize;
d(("orphan read\n"));
DevTermIO(pb, got);
}
else
{
dotracktype(pb, pkttyp, 0, 0, 0, 0, 1);
d(("packet thrown away...\n"));
}
}
}
/*E*/
/*
** 2nd level device command dispatcher (~SANA2IOF_QUICK)
*/
/*F*/ PRIVATE REGARGS VOID dos2reqs(BASEPTR)
{
struct IOSana2Req *ios2;
/*
** Every pending IO message will be GetMsg()'ed and processed. At the
** end of the loop it will be DevTermIO()'ed back to the sender,
** _but_only_if_ it is non-NULL. In such cases the message has been
** put in a separate queue to be DevTermIO()'ed later (i.e. CMD_WRITEs
** and similar stuff).
** You find the same mimique in the 1st level dispatcher (device.c)
*/
while(ios2 = (struct IOSana2Req *)GetMsg(pb->pb_ServerPort))
{
if (pb->pb_Flags & PLIPF_RECEIVING)
{
d(("incoming data!"));
break;
}
d(("sana2req %ld from serverport\n", ios2->ios2_Req.io_Command));
switch (ios2->ios2_Req.io_Command)
{
case S2_ONLINE:
if (!goonline(pb))
{
ios2->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
}
break;
case S2_OFFLINE:
gooffline(pb);
rejectpackets(pb); /* reject all pending requests */
break;
case S2_CONFIGINTERFACE:
if (pb->pb_Flags & PLIPF_NOTCONFIGURED)
{
#ifndef LINPLIP
memcpy(ios2->ios2_SrcAddr, pb->pb_SrcAddr, PLIP_ADDRFIELDSIZE);
memcpy(ios2->ios2_DstAddr, pb->pb_DstAddr, PLIP_ADDRFIELDSIZE);
#endif
if (!goonline(pb))
{
ios2->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
}
}
else
{
ios2->ios2_Req.io_Error = S2ERR_BAD_STATE;
ios2->ios2_WireError = S2WERR_IS_CONFIGURED;
}
break;
}
if (ios2) DevTermIO(pb,ios2);
}
}
/*E*/
/*
** startup,initialisation and termination functions
*/
/*F*/ PRIVATE struct PLIPBase *startup(void)
{
struct ServerStartup *ss;
struct Process *we;
struct PLIPBase *base;
LOCALSYSBASE;
we = (struct Process*)FindTask(NULL);
d(("waiting for startup msg...\n"));
WaitPort(&we->pr_MsgPort);
ss = (struct ServerStartup *)GetMsg(&we->pr_MsgPort);
base = ss->ss_PLIPBase;
base->pb_Startup = ss;
d(("go startup msg at %lx, PLIPBase is %lx\n", ss, ss->ss_PLIPBase));
/* we will keep the startup message, to inform mother if we
** really could come up or if we failed to obtain some
** resource.
*/
return base;
}
/*E*/
/*F*/ PRIVATE VOID readargs(BASEPTR)
{
struct RDArgs *rda;
struct PLIPConfig args = { 0 };
BPTR plipvar, oldinput;
d(("entered\n"));
if (plipvar = Open(CONFIGFILE, MODE_OLDFILE))
{
oldinput = SelectInput(plipvar);
rda = ReadArgs(TEMPLATE , (LONG *)&args, NULL);
if(rda)
{
if (args.timeout)
pb->pb_Timeout =
BOUNDS(*args.timeout, PLIP_MINTIMEOUT, PLIP_MAXTIMEOUT);
if (args.priority)
SetTaskPri((struct Task*)pb->pb_Server,
BOUNDS(*args.priority, PLIP_MINPRIORITY, PLIP_MAXPRIORITY));
if (args.mtu)
pb->pb_MTU = BOUNDS(*args.mtu, PLIP_MINMTU, PLIP_MAXMTU);
if (args.bps)
pb->pb_ReportBPS = BOUNDS(*args.bps, PLIP_MINBPS, PLIP_MAXBPS);
if (args.retries)
pb->pb_Retries =
BOUNDS(*args.retries, PLIP_MINRETRIES, PLIP_MAXRETRIES);
if (args.sendcrc)
pb->pb_Flags |= PLIPF_SENDCRC;
else
pb->pb_Flags &= ~PLIPF_SENDCRC;
if (args.collisiondelay)
pb->pb_CollisionDelay =
BOUNDS(*args.collisiondelay, PLIP_MINCOLLISIONDELAY,
PLIP_MAXCOLLISIONDELAY);
else
pb->pb_CollisionDelay = PLIP_DEFDELAY + (pb->pb_Unit ?
PLIP_DELAYDIFF : 0);
if (args.arbitrationdelay)
pb->pb_ArbitrationDelay =
BOUNDS(*args.collisiondelay, PLIP_MINARBITRATIONDELAY,
PLIP_MAXARBITRATIONDELAY);
else
pb->pb_ArbitrationDelay = PLIP_DEFARBITRATIONDELAY;
if (args.nospecialstats)
pb->pb_ExtFlags |= PLIPEF_NOSPECIALSTATS;
FreeArgs(rda);
}
Close(SelectInput(oldinput));
}
d(("timeout %ld, pri %ld, mtu %ld, bps %ld, retries %ld, flags %08lx, delay %ld\n",
pb->pb_Timeout, (LONG)pb->pb_Server->pr_Task.tc_Node.ln_Pri, pb->pb_MTU, pb->pb_ReportBPS, pb->pb_Retries,
pb->pb_Flags, pb->pb_CollisionDelay));
d(("left\n"));
}
/*E*/
/*F*/ PRIVATE BOOL init(BASEPTR)
{
BOOL rc = FALSE;
ULONG sigmask;
if ((pb->pb_IntSig = AllocSignal(-1)) != -1)
{
pb->pb_IntSigMask = 1L << pb->pb_IntSig;
if ((pb->pb_ServerPort = CreateMsgPort()))
{
if ((pb->pb_CollPort = CreateMsgPort()))
{
if ((pb->pb_TimeoutPort = CreateMsgPort()))
{
/* save old exception setup */
pb->pb_OldExcept = SetExcept(0, 0xffffffff); /* turn'em off */
pb->pb_OldExceptCode = pb->pb_Server->pr_Task.tc_ExceptCode;
pb->pb_OldExceptData = pb->pb_Server->pr_Task.tc_ExceptData;
/* create new exception setup */
pb->pb_Server->pr_Task.tc_ExceptCode = (APTR)&exceptcode;
pb->pb_Server->pr_Task.tc_ExceptData = (APTR)pb;
SetSignal(0, sigmask = (1 << pb->pb_TimeoutPort->mp_SigBit));
SetExcept(sigmask, sigmask);
/* enter port address */
pb->pb_CollReq.tr_node.io_Message.mn_ReplyPort = pb->pb_CollPort;
if (!OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest*)&pb->pb_CollReq, 0))
{
TimerBase = (struct Library *)pb->pb_CollReq.tr_node.io_Device;
/* preset the io command, this will never change */
pb->pb_CollReq.tr_node.io_Command = TR_ADDREQUEST;
/* setup the timeout stuff */
pb->pb_TimeoutReq.tr_node.io_Flags = IOF_QUICK;
pb->pb_TimeoutReq.tr_node.io_Message.mn_ReplyPort = pb->pb_TimeoutPort;
pb->pb_TimeoutReq.tr_node.io_Device = pb->pb_CollReq.tr_node.io_Device;
pb->pb_TimeoutReq.tr_node.io_Unit = pb->pb_CollReq.tr_node.io_Unit;
pb->pb_TimeoutReq.tr_node.io_Command = TR_ADDREQUEST;
pb->pb_TimeoutSet = 0xff;
readargs(pb);
d(("allocating 0x%lx/%ld bytes frame buffer\n",
sizeof(struct PLIPFrame)+pb->pb_MTU,
sizeof(struct PLIPFrame)+pb->pb_MTU));
if ((pb->pb_Frame = AllocVec((ULONG)sizeof(struct PLIPFrame) +
pb->pb_MTU, MEMF_CLEAR|MEMF_ANY)))
{
rc = TRUE;
}
else
{
d(("couldn't allocate frame buffer\n"));
}
}
else
{
d(("couldn't open timer.device"));
}
}
else
{
d(("no port for timeout handling\n"));
}
}
else
{
d(("no port for collision handling\n"));
}
}
else
{
d(("no server port\n"));
}
}
else
{
d(("no signal\n",rc));
}
d(("left %ld\n",rc));
return rc;
}
/*E*/
/*F*/ PRIVATE VOID cleanup(BASEPTR)
{
struct BufferManagement *bm;
gooffline(pb);
while(bm = (struct BufferManagement *)RemHead((struct List *)&pb->pb_BufferManagement))
FreeVec(bm);
if (pb->pb_Frame) FreeVec(pb->pb_Frame);
if (pb->pb_TimeoutPort)
{
/* restore old exception setup */
SetExcept(0, 0xffffffff); /* turn'em off */
pb->pb_Server->pr_Task.tc_ExceptCode = pb->pb_OldExceptCode;
pb->pb_Server->pr_Task.tc_ExceptData = pb->pb_OldExceptData;
SetExcept(pb->pb_OldExcept, 0xffffffff);
if (TimerBase)
{
WaitIO((struct IORequest*)&pb->pb_TimeoutReq);
CloseDevice((struct IORequest*)&pb->pb_CollReq);
}
DeleteMsgPort(pb->pb_TimeoutPort);
}
if (pb->pb_CollPort) DeleteMsgPort(pb->pb_CollPort);
if (pb->pb_ServerPort) DeleteMsgPort(pb->pb_ServerPort);
if (pb->pb_IntSig != -1) FreeSignal(pb->pb_IntSig);
if (pb->pb_Flags & PLIPF_REPLYSS)
{
Forbid();
ReplyMsg((struct Message*)pb->pb_Startup);
}
}
/*E*/
/*
** entry point, mainloop
*/
/*F*/ PUBLIC VOID SAVEDS ServerTask(void)
{
BASEPTR;
d(("server running\n"));
if (pb = startup())
{
/* if we fail to allocate all resources, this flag reminds cleanup()
** to ReplyMsg() the startup message
*/
pb->pb_Flags |= PLIPF_REPLYSS;
if (init(pb))
{
ULONG recv=0, portsigmask, timersigmask, wmask;
BOOL running, timerqueued = FALSE;
/* Ok, we are fine and will tell this mother personally :-) */
pb->pb_Startup->ss_Error = 0;
/* don't forget this, or we will have to keep a warm place */
/* in our coffin for the system */
pb->pb_Flags &= ~PLIPF_REPLYSS;
ReplyMsg((struct Message*)pb->pb_Startup);
portsigmask = 1 << pb->pb_ServerPort->mp_SigBit;
timersigmask = 1 << pb->pb_CollPort->mp_SigBit;
wmask = SIGBREAKF_CTRL_F | SIGBREAKF_CTRL_C | pb->pb_IntSigMask | portsigmask | timersigmask;
for(running=TRUE;running;)
{
d(("wmask is 0x%08lx\n", wmask));
if (!(pb->pb_Flags & PLIPF_RECEIVING))
recv = Wait(wmask);
else
SetSignal(0, pb->pb_IntSigMask);
/*if (recv & pb->pb_IntSigMask)*/
if (pb->pb_Flags & PLIPF_RECEIVING)
{
d(("received an interrupt\n"));
doreadreqs(pb);
}
if (recv & portsigmask)
{
d(("SANA-II request(s)\n"));
dos2reqs(pb);
}
if (recv & timersigmask)
{
/* pop message */
AbortIO((struct IORequest*)&pb->pb_CollReq);
WaitIO((struct IORequest*)&pb->pb_CollReq);
timerqueued = FALSE;
d(("timer wakeup\n"));
}
/* try now to do write requests (if any pending) */
if (!timerqueued)
{
dowritereqs(pb);
/* don't let the other side wait too long! */
if (pb->pb_Flags & PLIPF_RECEIVING)
{
d(("received an interrupt\n"));
SetSignal(0, pb->pb_IntSigMask);
doreadreqs(pb);
}
/*
** Possible a collision has occurred, which is indicated by a
** special flag in PLIPBase.
**
** Using timer.device we periodically will be waked up. This
** allows us to delay write packets in cases when we cannot get
** the line immediately.
**
** If client and server are very close together, regarding the point
** of performance, the same delay time could even force multiple
** collisions (at least theoretical, I made no practical tests).
** Probably a CSMA/CD-like random-timed delay would be ideal.
*/
if (pb->pb_Flags & PLIPF_COLLISION)
{
pb->pb_Flags &= ~PLIPF_COLLISION;
pb->pb_CollReq.tr_time.tv_secs = 0;
pb->pb_CollReq.tr_time.tv_micro = pb->pb_CollisionDelay;
SendIO((struct IORequest*)&pb->pb_CollReq);
timerqueued = TRUE;
}
}
if (recv & SIGBREAKF_CTRL_C)
{
d(("received break signal\n"));
running = FALSE;
}
}
if (timerqueued)
{
/* finnish pending timer requests */
AbortIO((struct IORequest*)&pb->pb_CollReq);
WaitIO((struct IORequest*)&pb->pb_CollReq);
}
}
else
d(("init() failed\n"));
d(("cleaning up\n"));
cleanup(pb);
/* Exec will enable it's scheduler after we're dead. */
Forbid();
/* signal mother we're done */
if (pb->pb_ServerStoppedSigMask)
Signal(pb->pb_Task, pb->pb_ServerStoppedSigMask);
pb->pb_Flags |= PLIPF_SERVERSTOPPED;
}
else
d(("no startup packet\n"));
}
/*E*/